/*
 * 创建日期 2005-10-25
 *
 * 更改所生成文件模板为
 * 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
package com.ctsi.commons.util.request;

import com.ctsi.commons.util.*;
import com.ctsi.commons.util.json.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.internet.MimeUtility;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author yueming
 * 
 *         更改所生成类型注释的模板为 窗口 > 首选项 > Java > 代码生成 > 代码和注释
 */
public class UtilHttp {
	private static final Logger log = LoggerFactory.getLogger(UtilHttp.class);


	public static boolean isMulti(HttpServletRequest request) {
		String contentType = request.getContentType();
		return ((contentType != null) && (contentType.startsWith("multipart/form-data")));
	}

	public static Cookie getCookObjByName(HttpServletRequest request, String name) {
		assert (name != null);
		Cookie[] cl = request.getCookies();
		if (cl != null) {
			for (Cookie cook : cl) {
				if (cook.getName().equals(name))
					return cook;
			}
		}

		return null;
	}

	public static Map<String, Object> filterBlank(Map<String, Object> map) {
		return map.entrySet().stream().parallel().filter(en -> {
                    if (en.getValue() instanceof String)
                    {
                        return !UtilValidate.isEmpty((String) en.getValue());
                    } else {
                        return en.getValue() != null;
                    }
            }).collect(Collectors.toMap(e -> e.getKey(),e -> e.getValue()));

	}

	public static String getCookByName(HttpServletRequest request, String name) {
		assert (name != null);
		Cookie[] cl = request.getCookies();
		if (cl != null) {
			for (Cookie cook : cl) {
				if (cook.getName().equals(name))
					return cook.getValue();
			}
		}

		return null;
	}

	/**
	 * 设置cookie
	 * 
	 * @param response
	 * @param name
	 * @param value
	 * @param path
	 * @param expiry
	 *            存活时间 秒
	 */
	public final static void setCookie(HttpServletResponse response, String name, String value, String path, String domain, int expiry) {

		Cookie cookie = new Cookie(name, value);
		cookie.setSecure(false);
		if (UtilValidate.isNotEmpty(path))
			cookie.setPath(path);
		else
			cookie.setPath("/");
		if (expiry > 0)
			cookie.setMaxAge(expiry);

		if (UtilValidate.isNotEmpty(domain))
			cookie.setDomain(domain);

		response.addCookie(cookie);
	}

    /**
     * 删除
     * @param request
     * @param response
     * @param name
     * @param path
     */
	public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String name, String path) {
		Cookie cookie = getCookObjByName(request, name);
		if (cookie != null) {
			cookie.setMaxAge(0);
			if (UtilValidate.isNotEmpty(path)) {
				cookie.setPath(path);
			} else {
				cookie.setPath("/");// 不设置为当前路径
			}
			response.addCookie(cookie);
		}
	}

	/**
	 * 获取request请求参数
	 * 
	 * @return The resulting Map
	 */
	public static Map<String, Object> getParameterMap(HttpServletRequest request, boolean htmlCheck) {
		Map<String, Object> paramMap = new HashMap<String, Object>();

		java.util.Enumeration<String> e = request.getParameterNames();
		while (e.hasMoreElements()) {
			String name = e.nextElement();
			Object value = null;
			String[] paramArr = request.getParameterValues(name);
			if (paramArr != null) {
				if (paramArr.length > 1) {
					if (htmlCheck)
						value = Arrays.asList(checkHtmlStrArray(paramArr));
					else
						value = Arrays.asList(paramArr);
				} else {
					if (htmlCheck) {
						value = StringUtil.htmlDecoding(paramArr[0]);
					} else
						value = paramArr[0];
				}
			}
			paramMap.put(name, value);
		}

		return paramMap;
	}

	public static Map<String, Object> getParameterMap(HttpServletRequest request) {
		return getParameterMap(request, false);
	}

	public static String[] checkHtmlStrArray(String[] strArry) {
		if (strArry == null || strArry.length == 0)
			return strArry;
		for (int i = 0; i < strArry.length; i++) {
			strArry[i] = StringUtil.htmlDecoding(strArry[i]);
		}
		return strArry;
	}

	/**
	 * 将多个参数编码在 url中
	 * 
	 * @param args
	 * @return
	 */
	public static String urlEncodeArgs(Map<String, ? extends Object> args) {
		return urlEncodeArgs(args, false);
	}

	/**
	 * 将多个参数编码在 url中
	 * 
	 * @param args
	 * @param useExpandedEntites
	 *            分割符号是否编码
	 * @return
	 */
	public static String urlEncodeArgs(Map<String, ? extends Object> args, boolean useExpandedEntites) {
		StringBuilder buf = new StringBuilder();
		if (args != null) {
			for (Map.Entry<String, ? extends Object> entry : args.entrySet()) {
				String name = entry.getKey();
				Object value = entry.getValue();
				String valueStr = null;
				if (name != null && value != null) {
					if (value instanceof String) {
						valueStr = (String) value;
					} else {
						valueStr = value.toString();
					}

					if (valueStr != null && valueStr.length() > 0) {
						if (buf.length() > 0) {
							if (useExpandedEntites) {
								buf.append("&amp;");
							} else {
								buf.append("&");
							}
						}
						try {
							buf.append(URLEncoder.encode(name, UtilFile.Charset_UTF8));
						} catch (UnsupportedEncodingException e) {
							throw new RuntimeException(e);
						}
						buf.append('=');
						try {
							buf.append(URLEncoder.encode(valueStr, UtilFile.Charset_UTF8));
						} catch (UnsupportedEncodingException e) {
							throw new RuntimeException(e);
						}
					}
				}
			}
		}
		return buf.toString();
	}

	/**
	 * 获取context,如果是根返回root root
	 * 
	 * @param request
	 *            An HttpServletRequest http请求
	 * @return String
	 */
	public static String getApplicationName(HttpServletRequest request) {
		String appName;
		if (request.getContextPath().length() > 1) {
			appName = request.getContextPath().substring(1);
		} else {
			appName = "/";
		}
		return appName;
	}

	public static String getApplicationName(String url) throws Exception {
		String appName;
		URI u = new URI(url);
		appName = u.getRawPath();

		if (UtilValidate.isEmpty(appName)) {
			appName = "/";
		} else {
			appName = StringUtil.split(appName, "/").get(0);
		}
		return appName;
	}

	/**
	 * 将参数放在request的属性中
	 * 
	 * @param request
	 */
	public static void parametersToAttributes(HttpServletRequest request) {
		java.util.Enumeration<String> e = request.getParameterNames();
		while (e.hasMoreElements()) {
			String name = e.nextElement();
			request.setAttribute(name, request.getParameter(name));
		}
	}

	public static String getReferer(HttpServletRequest request) {
		return request.getHeader("Referer");
	}

	public static StringBuilder getServerRootUrl(HttpServletRequest request) {
		StringBuilder requestUrl = new StringBuilder(request.getScheme());
		requestUrl.append("://").append(request.getServerName());
		if (request.getServerPort() != 80 && request.getServerPort() != 443)
			requestUrl.append(":").append(request.getServerPort());
		return requestUrl;
	}

	/**
	 * 获取完整context路径
	 * 
	 * @param request
	 * @return
	 */
	public static StringBuilder getServerContextUrl(HttpServletRequest request) {

		if (request.getContextPath().length() > 1) {
			return getServerRootUrl(request).append(request.getContextPath());

		} else {
			return getServerRootUrl(request);
		}

	}

	public static StringBuilder getFullRequestUrl(HttpServletRequest request) {
		StringBuilder requestUrl = UtilHttp.getServerRootUrl(request);
		requestUrl.append(request.getRequestURI());
		String queryStr = request.getQueryString();
		if (queryStr != null) {
			requestUrl.append('?');
			requestUrl.append(queryStr);
		}
		return requestUrl;
	}

	// ie6打开问题，有问题
	public static void setResponseBrowserProxyNoCache(HttpServletResponse response) {
		long nowMillis = System.currentTimeMillis();
		response.setDateHeader("Expires", nowMillis);
		response.setDateHeader("Last-Modified", nowMillis); // always modified
		response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); // HTTP/1.1
		response.addHeader("Cache-Control", "post-check=0, pre-check=0, false");
		response.setHeader("Pragma", "no-cache"); // HTTP/1.0
	}

	public static void setResponseBrowserProxyCache(HttpServletResponse response, int max_age) {
		long nowMillis = System.currentTimeMillis();
		response.setDateHeader("Last-Modified", nowMillis - max_age * 1000); //
		response.setDateHeader("Expires", nowMillis + max_age * 1000);
		response.setHeader("Cache-Control", "public,max-age=" + String.valueOf(max_age)); // HTTP/1.1

	}

	private static Map<String, String> mimeMap;

	private static String getUserMime(String fileName) {
		if (mimeMap == null) {
			synchronized (UtilHttp.class) {
				mimeMap = UtilResource.getPropertyMap(UtilHttp.class.getPackage().getName() + "/mime.properties", false);
			}

		}
		return mimeMap.get(UtilFile.getFileType(fileName));
	}

	public static Map<String, String> getUserMimeMap() {
		if (mimeMap == null) {
			synchronized (UtilHttp.class) {
				mimeMap = UtilResource.getPropertyMap(UtilHttp.class.getPackage().getName() + "/mime.properties", false);
			}

		}
		return mimeMap;
	}

	/**
	 * 获取文件mime类型
	 * 
	 * @param fileName
	 * @return
	 */
	public static String getContentTypeByFileName(String fileName) {
		FileNameMap mime = URLConnection.getFileNameMap();
		String result = mime.getContentTypeFor(fileName);
		if (result == null) {
			result = getUserMime(fileName);
		}
		if (result == null)
			result = HttpUnknownType;
		return result;
	}

	public static final String HttpUnknownType = "application/unknown";// application/x-download,application/force-download,www/unknowen

	public static void streamContentToBrowser(HttpServletResponse response, byte[] bytes, String contentType, String fileName, boolean preventCache) throws IOException {
		streamContentToBrowser(null, response, bytes, contentType, fileName, preventCache);
	}

	public static void streamContentToBrowser(HttpServletRequest request, HttpServletResponse response, byte[] bytes, String contentType, String fileName, boolean preventCache) throws IOException {
		if (preventCache)
			setResponseBrowserProxyNoCache(response);

		// set the response info
		response.setContentLength(bytes.length);
		if (contentType != null) {
			response.setContentType(contentType);
		}
		if (UtilValidate.isNotEmpty(fileName)) {

			setDownLoadFileName(request, response, fileName);

		} else {
			log.trace("file name is empty");
		}

		// create the streams
		OutputStream out = response.getOutputStream();
		InputStream in = new ByteInputStream(bytes);

		streamContent(out, in, bytes.length);

	}

	public static String getDomainFromRequest(HttpServletRequest request) {

		return getDomainOfServerName(request.getServerName(), true);
	}

	// 包含子域名需要以.开头
	public static String getDomainOfServerName(String host, boolean all_sub_domain) {
		int p = host.indexOf('.');
		if (p == -1 || p + 1 >= host.length()) {
			log.debug("域名无效:{}", host);
			return host;
		}

		if (isIPAddr(host))
			return host;

		if (all_sub_domain) {
			return host.substring(p);
		} else {
			return host.substring(p + 1);
		}

	}

	/**
	 * 判断字符串是否是一个IP地址
	 * 
	 * @param addr
	 * @return
	 */
	public static boolean isIPAddr(String addr) {
		if (UtilValidate.isEmpty(addr))
			return false;
		String[] ips = addr.split("\\.");
		if (ips.length != 4)
			return false;
		try {
			int ipa = Integer.parseInt(ips[0]);
			int ipb = Integer.parseInt(ips[1]);
			int ipc = Integer.parseInt(ips[2]);
			int ipd = Integer.parseInt(ips[3]);
			return ipa >= 0 && ipa <= 255 && ipb >= 0 && ipb <= 255 && ipc >= 0 && ipc <= 255 && ipd >= 0 && ipd <= 255;
		} catch (Exception e) {
		}
		return false;
	}

	public static void streamContentToBrowser(HttpServletResponse response, byte[] bytes, String contentType, String fileName) throws IOException {
		streamContentToBrowser(response, bytes, contentType, fileName, true);
	}

	public static void streamContentToBrowser(HttpServletResponse response, byte[] bytes) throws IOException {
		streamContentToBrowser(response, bytes, null, null, true);
	}

	/**
	 * 输出流
	 * 
	 * 
	 * @param response
	 *            response
	 * @param bytes
	 *            输出的数据
	 * @param contentType
	 *            内容类型
	 * @throws IOException
	 */
	public static void streamContentToBrowser(HttpServletResponse response, byte[] bytes, String contentType) throws IOException {
		streamContentToBrowser(response, bytes, contentType, null, true);
	}

	/**
	 * 流内容 从 InputStream 到 ServletOutputStream 会关闭 ServletOutputStream 当输出结束 。关闭 InputStream
	 * 
	 * @param request
	 *            请求对象
	 * @param response
	 *            HttpServletResponse object to get OutputStream from
	 * @param in
	 *            输出流
	 * @param length
	 *            输出长度
	 * @param contentType
	 *            内容类型
	 * @throws IOException
	 */
	public static void streamContentToBrowser(HttpServletRequest request, HttpServletResponse response, InputStream in, int length, String contentType, String fileName, boolean preventCache) throws IOException {
		if (preventCache)
			setResponseBrowserProxyNoCache(response);

		if (length > 0) {
			response.setContentLength(length);
		}
		if (contentType != null) {
			response.setContentType(contentType);
		}
		if (UtilValidate.isNotEmpty(fileName)) {

			setDownLoadFileName(request, response, fileName);

		} else {
			log.trace("file name is empty");
		}

		// stream the content
		OutputStream out = response.getOutputStream();

		streamContent(out, in, length);

	}

	public static void streamContentToBrowser(HttpServletRequest request, HttpServletResponse response, InputStream in, String contentType, String fileName) throws IOException {
		streamContentToBrowser(request, response, in, 0, contentType, fileName, true);
	}

	public static void streamContentToBrowser(HttpServletResponse response, InputStream in, int length, String contentType, String fileName, boolean preventCache) throws IOException {
		streamContentToBrowser(null, response, in, length, contentType, fileName, preventCache);
	}

	public static void streamContentToBrowser(HttpServletResponse response, InputStream in, int length, String contentType, String fileName) throws IOException {
		streamContentToBrowser(response, in, length, contentType, fileName, true);
	}

	public static void streamContentToBrowser(HttpServletResponse response, InputStream in, int length, String contentType) throws IOException {
		streamContentToBrowser(response, in, length, contentType, null, true);
	}

	/**
	 * 输出InputStream
	 * 
	 * 输入输出流会被关闭
	 * 
	 * @param out
	 *            输出流
	 * @param in
	 *            输入流
	 * @param length
	 *            长度
	 * @throws IOException
	 */
	public static void streamContent(OutputStream out, InputStream in, final int length) throws IOException {
		try {
			if (out == null) {
				throw new IOException("Attempt to write to null output stream");
			}

			if (in == null) {
				throw new IOException("Attempt to read from null input stream");
			}
			if (length < 1) {
				UtilFile.copyNoClose(in, out);
			} else {
				UtilFile.copy(in, out, length);
			}

		} catch (IOException e) {

			throw e;
		} finally {
			UtilMisc.closeObjNoExc(out);
			UtilMisc.closeObjNoExc(in);

		}
	}

	/**
	 * 获取IP 在使用反向代理时需要使用，解决使用apache代理获取IP，负载均衡下获取IP不正确的问题
	 * 
	 * @param request
	 * @return
	 */
	public static final String getIpAddr(HttpServletRequest request) {
		String ip = request.getHeader("X-Forwarded-For");// apache

		if (UtilValidate.isEmpty(ip) || "null".equalsIgnoreCase(ip)) {
			ip = request.getHeader("X-Real-IP");// nginx
		}
		if (UtilValidate.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	public static final String getPublicUrlUseRef(HttpServletRequest request) {
		final String refUrl = UtilHttp.getReferer(request);
		if (UtilValidate.isEmpty(refUrl)) {
			log.info(" uri :{} proxy no Referer", request.getRequestURI());
			return request.getRequestURL().substring(0, request.getRequestURL().indexOf(request.getRequestURI())) + request.getContextPath() + "/";
		} else {
			URL u;
			try {
				u = new URL(refUrl);
			} catch (MalformedURLException e) {
				log.error("URL error", e);
				throw new RuntimeException(e);
			}
			String path = u.getPath();

			String appName = null;
			if (UtilValidate.isNotEmpty(path)) {
				int secsp = path.indexOf('/', 1);
				if (secsp > -1) {
					appName = path.substring(0, secsp);
				} else {
					log.info("  path {} no conter app name", path);
					appName = path;
				}
			} else {
				log.warn("refUrl :{} no path", refUrl);
				appName = "";
			}

			StringBuilder newurl = new StringBuilder(200);
			newurl.append(refUrl, 0, refUrl.indexOf(u.getPath()));

			newurl.append(appName).append('/');
			return newurl.toString();
		}
	}
	public static void setDownLoadFileName(final HttpServletRequest request, final HttpServletResponse response,final String filename ) {
		setDownLoadFileName( request,  response,  filename,null);
	}

	/**
	 * 设置浏览器文件下载名称
	 * 
	 * @param request
	 * @param response
	 * @param filename
	 */
	public static final void setDownLoadFileName(final HttpServletRequest request, final HttpServletResponse response,final String filename,String disposition) {
		String userAgent = request == null ? null : request.getHeader("User-Agent");
		String rtn = null;
		try {

			String new_filename;
			if (userAgent != null) {

				userAgent = userAgent.toLowerCase();
				// IE浏览器，只能采用URLEncoder编码
				if (userAgent.indexOf("msie") != -1) {
					new_filename = URLEncoder.encode(filename, UtilFile.Charset_UTF8);
					rtn = "filename=\"" + new_filename + "\"";
				}
				// FireFox浏览器，可以使用MimeUtility或filename*或ISO编码的中文输出
				else if (userAgent.indexOf("mozilla") != -1) {
					new_filename = URLEncoder.encode(filename, UtilFile.Charset_UTF8);
					rtn = "filename*=UTF-8''" + new_filename;
					// new_filename = MimeUtility.encodeText(filename, "UTF8", "B");
					// rtn = "filename=\"" + new_filename + "\"";
				}
				// Opera浏览器只能采用filename*
				else if (userAgent.indexOf("opera") != -1) {
					new_filename = URLEncoder.encode(filename, UtilFile.Charset_UTF8);
					rtn = "filename*=UTF-8''" + new_filename;
				}
				// Safari浏览器，只能采用ISO编码的中文输出
				else if (userAgent.indexOf("safari") != -1) {
					rtn = "filename=\"" + new String(filename.getBytes(StandardCharsets.UTF_8), "ISO8859-1") + "\"";
				}
				// Chrome浏览器，只能采用MimeUtility编码或ISO编码的中文输出
				else if (userAgent.indexOf("webkit") != -1) {
					new_filename = MimeUtility.encodeText(filename, "UTF8", "B");
					rtn = "filename=\"" + new_filename + "\"";
				}

			} else {
				new_filename = URLEncoder.encode(filename, UtilFile.Charset_UTF8);
				rtn = "filename=\"" + new_filename + "\"";
			}
			log.debug("useragent :{} filename :{}", userAgent, rtn);
		} catch (UnsupportedEncodingException e1) {
			throw new RuntimeException(e1);
		}
		if(UtilValidate.isEmpty(disposition)){
			disposition="attachment;";
		}
		response.addHeader("Content-Disposition", disposition + rtn);
	}

	public static final String jsonMimeAndCharSet = "application/json;charset=UTF-8";

	public static final void outJson(final Object obj, final HttpServletResponse response) {

		try {
			response.setContentType(jsonMimeAndCharSet);
			JsonUtil.toJsonAppend(obj, response.getWriter());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

	}

	public static final String getRequestPath(final HttpServletRequest request) {
		String u = request.getRequestURI();
		return u.substring(0, u.lastIndexOf('/') + 1);

	}

	public static final String getRequestContextPath(final HttpServletRequest request) {
		String u = request.getContextPath();
		if (UtilValidate.isEmpty(u)) {
			u = "/";
		} else {
			if (u.endsWith("/")) {
				return u;
			} else {
				return u + "/";
			}
		}
		return u;

	}
	
	public static final String getContent_Type(HttpServletResponse response) {
		String result = response.getContentType(); 
		if (UtilValidate.isEmpty(result)) {
			result = response.getHeader("Content-Type");
			if (UtilValidate.isEmpty(result)) {
				result = response.getHeader("content-Type");
			}
		}
		return result;

	}
}
